/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;
using ELE.EnterpriseLibrary.Data.Configuration.Design.Properties;
using System.Globalization;

namespace ELE.EnterpriseLibrary.Data.Configuration.Design
{	
	class ObjectMappingSettingsNodeBuilder : NodeBuilder
	{
		private ObjectMappingSettings objectMappingSettings;
    private SqlQueryNode defaultQueryNode;
    private ObjectSqlMappingNode defaultMappingNode;		

		public ObjectMappingSettingsNodeBuilder(IServiceProvider serviceProvider, ObjectMappingSettings objectMappingSettings) : base(serviceProvider)
		{
			this.objectMappingSettings = objectMappingSettings;			
		}

		public ObjectMappingSettingsNode Build()
		{
			ObjectMappingSettingsNode rootNode = new ObjectMappingSettingsNode();

      QueryBuilderCollectionNode queryBuilders = new QueryBuilderCollectionNode();
      foreach (QueryBuilderData data in objectMappingSettings.QueryBuilders)
      {
        CreateQueryBuilderNode(queryBuilders, data);
      }
      rootNode.AddNode(queryBuilders);

      ObjectTypeAssemblyCollectionNode assemblies = new ObjectTypeAssemblyCollectionNode();
      ObjectTypeDefCollectionNode types = new ObjectTypeDefCollectionNode();
      if (objectMappingSettings.Objects != null)
      {
        foreach (AssemblyData data in objectMappingSettings.Objects.Assemblies)
        {
          CreateAssemblyNode(assemblies, data);
        }
        foreach (TypeData data in objectMappingSettings.Objects.Types)
        {
          CreateTypeNode(assemblies, types, data);
        }
      }
      rootNode.AddNode(assemblies);
      rootNode.AddNode(types);

      ObjectSqlMappingCollectionNode mNode = new ObjectSqlMappingCollectionNode();
      foreach (MappingQueryData data in objectMappingSettings.Mappings)
      {
        CreateObjectMappingNode(types, queryBuilders, mNode, data);
      }
      rootNode.AddNode(mNode);
      rootNode.DefaultObjectSqlMapping = defaultMappingNode;

      SqlQueryCollectionNode node = new SqlQueryCollectionNode();
      foreach (SqlQueryData data in objectMappingSettings.Queries)
      {
        CreateSqlQueryNode(queryBuilders, mNode, node, data);
      }
      rootNode.AddNode(node);
      rootNode.DefaultSqlQuery = defaultQueryNode;

			return rootNode;
		}

    private void CreateSqlQueryNode(QueryBuilderCollectionNode queryBuilders,
                                    ObjectSqlMappingCollectionNode mappings,
                                    SqlQueryCollectionNode node, 
                                    SqlQueryData sqlQueryData)
		{
			SqlQueryNode sqlNode = new SqlQueryNode(sqlQueryData);
      if (!string.IsNullOrEmpty(sqlQueryData.QueryBuilder))
      {
        foreach (QueryBuilderNode qbNode in queryBuilders.Nodes)
        {
          if (sqlQueryData.QueryBuilder == qbNode.Name)
          {
            sqlNode.QueryBuilder = qbNode;
            break;
          }
        }
      }
      if (sqlNode.Nodes != null)
      {
        foreach (ObjectMappingReferenceNode map in sqlNode.Nodes)
        {
          foreach (ObjectSqlMappingNode mNode in mappings.Nodes)
          {
            if (map.MapName == mNode.Name)
            {
              if (mNode.Nodes != null)
              {
                foreach (ObjectMappingNode oNode in mNode.Nodes)
                {
                  if (oNode.Name == map.ObjectName)
                  {
                    map.Mapping = oNode;
                    // Find the link to the overwritten bindings
                    if (map.Nodes != null && oNode.Nodes != null)
                    {
                      bool found = false;
                      foreach(ObjectMappingTableNode tNode in oNode.Nodes)
                      {
                        if (tNode.Nodes != null)
                        {
                          foreach (ObjectBindingNode bNode in tNode.Nodes)
                          {
                            foreach (ObjectBindingOverwriteNode bind in map.Nodes)
                            {
                              if (bind.Name == bNode.Name)
                              {
                                bind.Binding = bNode;
                                found = true;
                                break;
                              }
                            }
                            if (found)
                            {
                              break;
                            }
                          }
                        }
                        if (found)
                        {
                          break;
                        }
                      }
                    }
                    break;
                  }
                }
              }
              break;
            }
          }
        }
      }
      node.AddNode(sqlNode);
      if (sqlNode.Name == objectMappingSettings.DefaultQuery) defaultQueryNode = sqlNode;
    }
    private void CreateObjectMappingNode( ObjectTypeDefCollectionNode types, 
                                          QueryBuilderCollectionNode queryBuilders,
                                          ObjectSqlMappingCollectionNode node, 
                                          MappingQueryData objectMappingData)
    {
      ObjectSqlMappingNode mappingNode = new ObjectSqlMappingNode(objectMappingData);
      if (types != null && types.Nodes != null)
      {
        if (mappingNode.Nodes != null)
        {
          foreach (ObjectMappingNode omNode in mappingNode.Nodes)
          {
            foreach (ObjectTypeDefNode typeNode in types.Nodes)
            {
              if (typeNode.Name == omNode.Name)
              {
                omNode.Type = typeNode;
                break;
              }
            }
          }
        }
      }
      if (!string.IsNullOrEmpty(objectMappingData.QueryBuilder))
      {
        foreach (QueryBuilderNode qbNode in queryBuilders.Nodes)
        {
          if (qbNode.Name == objectMappingData.QueryBuilder)
          {
            mappingNode.QueryBuilder = qbNode;
            break;
          }
        }
      }
      node.AddNode(mappingNode);
      if (mappingNode.Name == objectMappingSettings.DefaultMapping) defaultMappingNode = mappingNode;
    }
    private void CreateQueryBuilderNode(QueryBuilderCollectionNode node, QueryBuilderData data)
    {
      QueryBuilderNode subNode = new QueryBuilderNode(data);
      node.AddNode(subNode);
    }
    private void CreateAssemblyNode(ObjectTypeAssemblyCollectionNode node, AssemblyData data)
    {
      ObjectTypeAssemblyNode subNode = new ObjectTypeAssemblyNode(data);
      node.AddNode(subNode);
    }
    private void CreateTypeNode(ObjectTypeAssemblyCollectionNode assemblies, ObjectTypeDefCollectionNode node, TypeData data)
    {
      ObjectTypeDefNode subNode = new ObjectTypeDefNode(data);
      if (!string.IsNullOrEmpty(data.Assembly) && assemblies != null && assemblies.Nodes != null)
      {
        foreach (ObjectTypeAssemblyNode assemblyNode in assemblies.Nodes)
        {
          if (assemblyNode.Name == data.Assembly)
          {
            subNode.Assembly = assemblyNode;
            subNode.Type = assemblyNode.Assembly.GetType(data.Type);
            break;
          }
        }
      }
      node.AddNode(subNode);
    }

	}
}
